package com.secsbrain.development.ribbon;

import com.alibaba.fastjson.JSON;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.*;
import com.secsbrain.cmdb.client.iface.OpsInfoService;
import com.secsbrain.cmdb.client.model.DeployEngineDto;
import com.secsbrain.common.ResponseVo;
import com.secsbrain.common.utils.RedisUtil;
import com.secsbrain.common.utils.SpringContext;
import com.secsbrain.development.dal.config.RibbonResolveMapConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @author yjy
 * @date 2019/12/25 18:02
 **/
public class DirectResolveRule extends AbstractLoadBalancerRule {

    private static Logger log = LoggerFactory.getLogger(DirectResolveRule.class);

    /**
     * 默认负载策略
     */
    private IRule defaultRule;
    private RibbonResolveMapConfig ribbonResolveMapConfig;

    public DirectResolveRule() {
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }

    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }
        return Optional.of(ribbonResolveMapConfig.isEnabled() && lb instanceof ZoneAwareLoadBalancer)
                .filter(Boolean::booleanValue)
                .flatMap(bool -> {
                    String serviceName = ((ZoneAwareLoadBalancer) lb).getName();
                    if (key == null) {
                        Map<String, String> servers = ribbonResolveMapConfig.getServers();
                        return servers.entrySet().stream()
                                .filter(entry -> entry.getKey().equalsIgnoreCase(serviceName))
                                .map(entry -> {
                                    String[] address = entry.getValue().split(":");
                                    int port = address.length == 1 ? 80 : Integer.parseInt(address[1]);
                                    Server server = new Server(address[0], port);
                                    server.setReadyToServe(true);
                                    server.setAlive(true);
                                    if (ribbonResolveMapConfig.isLogChooseServer()) {
                                        log.info("Choose direct resolve service provider [{}] > [{}]", serviceName, server);
                                    }
                                    return server;
                                }).findFirst();
                    } else {
                        List keyList = (List) key;
                        DeployEngineDto deployEngineDto = this.getDeployEngine((String) keyList.get(0));
                        String deployUrl = deployEngineDto.getDeployUrl();
                        String url = deployUrl.split("://")[1];
                        String[] address = url.split(":");
                        int port = address.length == 1 ? 80 : Integer.parseInt(address[1]);
                        String host = address[0];
                        String hostFinal = host.contains("/") ? host.substring(0, host.indexOf("/")) : host;
                        Server server = new Server(hostFinal, port);
                        return Optional.of(server);
                    }

                })
                .orElseGet(() -> {
                    Server server;
                    // 采用默认算法
                    if (defaultRule != null) {
                        defaultRule.setLoadBalancer(lb);
                        server = defaultRule.choose(key);
                    } else {
                        server = new RoundRobinRule(lb).choose(key);
                    }
                    if (ribbonResolveMapConfig.isLogChooseServer()) {
                        if (lb instanceof ZoneAwareLoadBalancer) {
                            String serviceName = ((ZoneAwareLoadBalancer) lb).getName();
                            log.info("Choose ZoneAwareLoadBalancer service provider [{}] > [{}]", serviceName, server);
                        } else {
                            log.info("Choose lb service provider [{}]", server);
                        }
                    }
                    return server;
                });
    }

    public IRule getDefaultRule() {
        return defaultRule;
    }

    public void setDefaultRule(IRule defaultRule) {
        this.defaultRule = defaultRule;
    }

    public RibbonResolveMapConfig getRibbonResolveMapConfig() {
        return ribbonResolveMapConfig;
    }

    public void setRibbonResolveMapConfig(RibbonResolveMapConfig ribbonResolveMapConfig) {
        this.ribbonResolveMapConfig = ribbonResolveMapConfig;
    }

    /**
     * 获取发布引擎
     *
     * @param envName 环境名
     */
    private DeployEngineDto getDeployEngine(String envName) {
        String key = DeployEngineDto.APP_REDIS_PREX + "new1_" + envName;
        boolean exists = RedisUtil.kExists(key);
        if (exists) {
            String resJson = RedisUtil.get(key);
            log.debug("缓存的发布信息：{}", resJson);
            return JSON.parseObject(resJson, DeployEngineDto.class);
        }
        OpsInfoService opsInfoService = SpringContext.getBean(OpsInfoService.class);
        ResponseVo<DeployEngineDto> deployEngineDtoResponseVo = opsInfoService.queryDeployEngineByEnv(envName);
        if (deployEngineDtoResponseVo.isSuccess()) {
            RedisUtil.put(key, JSON.toJSONString(deployEngineDtoResponseVo.getData()), 60 * 60);//缓存1小时
            return deployEngineDtoResponseVo.getData();
        }
        throw new RuntimeException("查询发布引擎信息失败：" + deployEngineDtoResponseVo.getMsg());
    }

    @Override
    public String toString() {
        return "DirectResolveRule{" +
                "defaultRule=" + defaultRule +
                ", ribbonResolveMapConfig=" + ribbonResolveMapConfig +
                '}';
    }
}